package com.dzu.gulimall.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dzu.common.exception.NoStockException;
import com.dzu.common.to.OrderTo;
import com.dzu.common.to.SkuHasStockVo;
import com.dzu.common.utils.PageUtils;
import com.dzu.common.utils.Query;
import com.dzu.common.utils.R;
import com.dzu.common.vo.MemberResponseVo;
import com.dzu.gulimall.order.constant.OrderConstant;
import com.dzu.gulimall.order.dao.OrderDao;
import com.dzu.gulimall.order.entity.OrderEntity;
import com.dzu.gulimall.order.entity.OrderItemEntity;
import com.dzu.gulimall.order.enums.OrderStatusEnum;
import com.dzu.gulimall.order.feign.CartFeignService;
import com.dzu.gulimall.order.feign.MemberFeignService;
import com.dzu.gulimall.order.feign.ProductFeignService;
import com.dzu.gulimall.order.feign.WareFeignService;
import com.dzu.gulimall.order.interceptor.LoginUserInterceptor;
import com.dzu.gulimall.order.service.OrderItemService;
import com.dzu.gulimall.order.service.OrderService;
import com.dzu.gulimall.order.to.OrderCreateTo;
import com.dzu.gulimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> threadLocal = new ThreadLocal<>();

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartFeignService cartFeignService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderItemService orderItemService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo vo = new OrderConfirmVo();

        MemberResponseVo member = LoginUserInterceptor.threadLocal.get();

        // 获取主线程的请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            // 每一个线程都来共享主线程请求
            RequestContextHolder.setRequestAttributes(requestAttributes);

            // 1、远程查询所有的收货地址列表
            List<MemberAddressVo> addressList = memberFeignService.getAddress(member.getId());
            vo.setMemberAddressVos(addressList);
        }, threadPoolExecutor);


        CompletableFuture<Void> orderItemFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);

            // 2、远程查询购物车所有选中的购物项
            List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
            vo.setItems(userCartItems);
            // feign在远程调用之前要构造请求，调用很多的拦截器
            // requestInterceptors
        }, threadPoolExecutor).thenRunAsync(() -> {
            List<OrderItemVo> itemList = vo.getItems();
            List<Long> skuIdList = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());

            R r = wareFeignService.getSkuHasStock(skuIdList);
            List<SkuHasStockVo> list = r.getData("data", new TypeReference<List<SkuHasStockVo>>() {
            });
            Map<Long, Boolean> map = list.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
            vo.setStocks(map);

        }, threadPoolExecutor);

        // 3、查询用户积分信息
        vo.setIntegration(member.getIntegration());

        // 4、其他数据 自动计算
        // 5、防重令牌
        String orderToken = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId(), orderToken, 30, TimeUnit.SECONDS);
        vo.setOrderToken(orderToken);

        CompletableFuture.allOf(addressFuture, orderItemFuture).get();
        return vo;
    }

    // 本地事务、在分布式系统下，只能控制住自己的回滚，控制不了其他服务的回滚
    // 分布式事务、最大原因。网络问题+分布式机器
//    @GlobalTransactional // 高并发不行
    @Transactional
    @Override
    public OrderSubmitResponseVo submitOrder(OrderSubmitVo vo) {
        MemberResponseVo member = LoginUserInterceptor.threadLocal.get();
        threadLocal.set(vo);

        OrderSubmitResponseVo resp = new OrderSubmitResponseVo();


        //1、验证令牌、 令牌的对比和删除必须是原子性操作
        String orderToken = vo.getOrderToken();
        // 0令牌失败  1删除成功
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId()), orderToken);
        if (result == null || result == 0L) {

            // 订单验证失败
            resp.setCode(1);
            return resp;
        }

        // 下单：创建订单、验证令牌、验证价格、锁定库存

        // 创建订单、订单项信息
        OrderCreateTo order = createOrder();
        // 验证价格
        OrderEntity orderEntity = order.getOrder();
        BigDecimal payAmount = orderEntity.getPayAmount();
        BigDecimal payPrice = vo.getPayPrice();
        if (Math.abs(NumberUtil.sub(payAmount, payPrice).doubleValue()) < 0.01) {
            // 对比没问题.
            // 保存订单
            saveOrder(order);

            // 锁定库存
            WareSkuLockVo skuLockVo = new WareSkuLockVo();
            skuLockVo.setOrderSn(orderEntity.getOrderSn());
            skuLockVo.setLocks(order.getOrderItems().stream().map(item -> {
                OrderItemVo orderItemVo = new OrderItemVo();
                orderItemVo.setSkuId(item.getSkuId());
                orderItemVo.setCount(item.getSkuQuantity());
                orderItemVo.setTitle(item.getSkuName());
                return orderItemVo;
            }).collect(Collectors.toList()));

            // 远程锁库存
            // 库存成功了，但是网络原因超时，订单回滚，库存不滚

            // 为了保证高并发。库存服务自己回滚，可以发消息给库存服务
            // 库存服务本身也可以使用自动解锁模式。使用消息队列
            R r = wareFeignService.orderLockStock(skuLockVo);
            if (r.getCode() == 0) {
                resp.setOrder(orderEntity);
                resp.setCode(0);
                // TODO 远程扣减积分
//                int i = 1 / 0; // 此处出现异常，订单回滚，库存不会滚
                // 订单创建成功，发送消息给mq
                rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", orderEntity);
                return resp;
            } else {
                String msg = (String) r.get("msg");
                throw new NoStockException(msg);
            }

        } else {
            resp.setCode(2);
            return resp;
        }
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        return this.getOne(new LambdaQueryWrapper<OrderEntity>().eq(OrderEntity::getOrderSn, orderSn));
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        Long orderId = orderEntity.getId();
        OrderEntity order = this.getById(orderId);
        if (order == null) {
            return;
        }

        if (order.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {
            OrderEntity update = new OrderEntity();
            update.setId(orderId);
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            OrderTo orderTo = BeanUtil.copyProperties(order, OrderTo.class);
            // 给MQ发一个
            try {
                // TODO 保证消息一定会发送出去，每一个消息都可以做好日志记录
                rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", orderTo);
            } catch (Exception e) {
                // TODO　将没发送成功的消息进行重试发送
            }
        }
    }

    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        this.save(orderEntity);

        orderItemService.saveBatch(order.getOrderItems());

    }

    private OrderCreateTo createOrder() {
        OrderCreateTo resp = new OrderCreateTo();
        // 1生成订单号
        String orderSn = IdWorker.getTimeId();
        OrderEntity orderEntity = buildOrder(orderSn);
        resp.setOrder(orderEntity);

        // 2、获取所有的订单项
        List<OrderItemEntity> orderItemEntityList = buildOrderItemList(orderSn);
        resp.setOrderItems(orderItemEntityList);

        // 3、计算价格相关
        computePrice(orderEntity, orderItemEntityList);

        resp.setFare(orderEntity.getFreightAmount());
        resp.setPayPrice(orderEntity.getPayAmount());
        return resp;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntityList) {

        BigDecimal coupon = BigDecimal.ZERO;
        BigDecimal integration = BigDecimal.ZERO;
        BigDecimal promotion = BigDecimal.ZERO;
        BigDecimal total = BigDecimal.ZERO;

        int giftIntegration = 0;
        int giftGrowth = 0;

        for (OrderItemEntity item : orderItemEntityList) {
            coupon = NumberUtil.add(coupon, item.getCouponAmount());
            integration = NumberUtil.add(integration, item.getIntegrationAmount());
            promotion = NumberUtil.add(promotion, item.getPromotionAmount());
            total = NumberUtil.add(total, item.getRealAmount());
            giftIntegration += item.getGiftIntegration();
            giftGrowth += item.getGiftGrowth();
        }

        // 1、订单价格相关
        orderEntity.setCouponAmount(coupon);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setTotalAmount(total);

        // 应付金额
        orderEntity.setPayAmount(NumberUtil.add(total, orderEntity.getFreightAmount()));

        // 积分
        orderEntity.setIntegration(giftIntegration);
        orderEntity.setGrowth(giftGrowth);

    }

    /**
     * 构建订单信息
     * @param orderSn
     * @return
     */
    private OrderEntity buildOrder(String orderSn) {
        // 创建订单号
        OrderEntity orderEntity = new OrderEntity();
        MemberResponseVo memberResponseVo = LoginUserInterceptor.threadLocal.get();
        orderEntity.setMemberId(memberResponseVo.getId());
        orderEntity.setMemberUsername(memberResponseVo.getUsername());

        orderEntity.setOrderSn(orderSn);

        // 获取收货地址信息
        OrderSubmitVo orderSubmitVo = threadLocal.get();

        FareVo fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        // 设置运费信息
        orderEntity.setFreightAmount(fare.getFare());

        // 设置的收货人信息
        MemberAddressVo address = fare.getAddress();
        orderEntity.setReceiverName(address.getName());
        orderEntity.setReceiverPhone(address.getPhone());
        orderEntity.setReceiverPostCode(address.getPostCode());
        orderEntity.setReceiverProvince(address.getProvince());
        orderEntity.setReceiverCity(address.getCity());
        orderEntity.setReceiverRegion(address.getRegion());
        orderEntity.setReceiverDetailAddress(address.getDetailAddress());

        // 设置订单初始状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);
        orderEntity.setDeleteStatus(0);

        return orderEntity;
    }

    /**
     * 构建所有的订单项
     *
     * @return
     */
    private List<OrderItemEntity> buildOrderItemList(String orderSn) {

        // 最后确定每个购物项的价格
        List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
        if (CollUtil.isNotEmpty(userCartItems)) {
            return userCartItems.stream()
                    .map(item -> {
                        OrderItemEntity entity = buildOrderItem(item);
                        entity.setOrderSn(orderSn);
                        return entity;
                    }).collect(Collectors.toList());

        }
        return Collections.emptyList();
    }


    /**
     * 构建单个订单项
     *
     * @param item
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo item) {
        OrderItemEntity entity = new OrderItemEntity();
        // 1、订单信息，订单号
        // 2、商品的spu信息
        Long skuId = item.getSkuId();
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfoVo = r.getData(new TypeReference<SpuInfoVo>() {
        });
        entity.setSpuId(spuInfoVo.getId());
        entity.setSpuName(spuInfoVo.getSpuName());
        entity.setSpuBrand(spuInfoVo.getBrandId().toString());
        entity.setCategoryId(spuInfoVo.getCatalogId());
        // 3、商品的sku信息
        entity.setSkuId(skuId);
        entity.setSkuName(item.getTitle());
        entity.setSkuPic(item.getImage());
        entity.setSkuPrice(item.getPrice());
        entity.setSkuAttrsVals(StrUtil.join(";", item.getSkuAttr()));
        entity.setSkuQuantity(item.getCount());

        // 4、优惠信息 todo
        // 5、积分信息
        int gift = NumberUtil.mul(item.getPrice(), item.getCount()).intValue();
        entity.setGiftGrowth(gift);
        entity.setGiftIntegration(gift);

        // 6、订单项的价格信息 (暂时写死为0
        entity.setPromotionAmount(BigDecimal.ZERO);
        entity.setIntegrationAmount(BigDecimal.ZERO);
        entity.setCouponAmount(BigDecimal.ZERO);
        entity.setRealAmount(NumberUtil.mul(entity.getSkuPrice(), entity.getSkuQuantity()));

        return entity;
    }
}
